<?php

namespace WS;

class WebsocketClient
{
    private $socket = null;

    /**
     * Конструктор.
     *
     * @param string $host
     * @param number $port
     */
    public function __construct ($host, $port) {
        $this->connect($host, $port);
    }

    /**
     * Деструктор.
     */
    public function __destruct() {
        $this->disconnect();
    }

    public function getCurrentWeather () {
        return $this->call();
    }

    /**
     * Вызов произвольного метода.
     *
     * @param string $method
     * @param array $params
     * @return mixed|string
     * @throws \Exception
     */
    protected function call ($method = null, array $params = null) {
        $request = array_slice(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT), 1, 1);
        if (func_num_args() > 0 && !is_string($method)) {
            throw new \Exception('');
        } elseif (func_num_args() === 0) {
            $method = isset($request[0]['function']) ? $request[0]['function'] : 'call';
        }
        $params = isset($request[0]['args']) ? $request[0]['args'] : [];
        foreach ($params as &$param) {
            $param = json_encode($param);
        }
        $params = implode(', ', $params);
        $data = get_class($this) . '::' . $method . '(' . $params . ')';
        fwrite($this->socket, "\x00" . $data . "\xff" );
        $response = '';
        while (substr($response, 0, 1) !== "\x00" ||
            substr($response, -1, 1) !== "\xff") {
            $response .= fread($this->socket, 2000);
        }
        $response = json_decode(trim($response, "\x00\xff"), JSON_OBJECT_AS_ARRAY);
        return $response;
    }

    /**
     * @param string $host
     * @param integer $port
     * @return boolean
     */
    protected function connect ($host, $port) {
        $key1 = $this->generateRandomString(32);
        $key2 = $this->generateRandomString(32);
        $key3 = $this->generateRandomString(8, false, true);
        // Готовим заголовок соединения.
        $header  = "GET /echo HTTP/1.1\r\n";
        $header .= "Upgrade: WebSocket\r\n";
        $header .= "Connection: Upgrade\r\n";
        $header .= "Host: ".$host.":".$port."\r\n";
        $header .= "Origin: http://localhost/\r\n";
        $header .= "Sec-WebSocket-Key1: " . $key1 . "\r\n";
        $header .= "Sec-WebSocket-Key2: " . $key2 . "\r\n";
        $header .= "\r\n";
        $header .= $key3;

        $this->socket = fsockopen($host, $port, $errno, $errstr, 2);
        fwrite($this->socket, $header) or die('Error: ' . $errno . ':' . $errstr);
        $response = fread($this->socket, 2000);

        /**
         * @todo: check response here. Currently not implemented cause "2 key handshake" is already deprecated.
         * See: http://en.wikipedia.org/wiki/WebSocket#WebSocket_Protocol_Handshake
         */

        return true;
    }

    /**
     * Разорвать соединение с сервисом.
     */
    protected function disconnect () {
        fclose($this->socket);
    }

    private function generateRandomString ($length = 10, $addSpaces = true, $addNumbers = true) {
        $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}';
        $useChars = array();
        // select some random chars:
        for($i = 0; $i < $length; $i++) {
            $useChars[] = $characters[mt_rand(0, strlen($characters)-1)];
        }
        // add spaces and numbers:
        if($addSpaces === true)
        {
            array_push($useChars, ' ', ' ', ' ', ' ', ' ', ' ');
        }
        if($addNumbers === true)
        {
            array_push($useChars, rand(0,9), rand(0,9), rand(0,9));
        }
        shuffle($useChars);
        $randomString = trim(implode('', $useChars));
        $randomString = substr($randomString, 0, $length);
        return $randomString;
    }
}